import { NativeModules, NativeEventEmitter, Platform } from "react-native";
import {
  IrohClient,
  IrohConfig,
  IrohNode,
  IrohDocument,
  IrohBlob,
  FileTransferProgress,
} from "./index";

// Get the native module
const NativeIroh = NativeModules.Iroh;

// Create event emitter for progress updates
const eventEmitter = new NativeEventEmitter(NativeIroh);

/**
 * Native implementation of Iroh client using platform-specific modules
 *
 * This implementation bridges to the actual iroh-ffi native modules:
 * - iOS: Swift implementation using iroh-ffi Swift bindings
 * - Android: Kotlin implementation using iroh-ffi Kotlin bindings
 */
export class IrohNativeClient implements IrohClient {
  private progressCallbacks: Map<
    string,
    (progress: FileTransferProgress) => void
  > = new Map();

  constructor() {
    // Set up progress event listener
    eventEmitter.addListener("transferProgress", (event) => {
      // Notify all active progress callbacks
      this.progressCallbacks.forEach((callback) => {
        callback({
          transferred: event.transferred,
          total: event.total,
          percentage: event.percentage,
          status: event.status,
        });
      });
    });
  }

  async createNode(config?: IrohConfig): Promise<IrohNode> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    const nodeData = await NativeIroh.createNode(config || {});
    return {
      id: nodeData.id,
      isConnected: nodeData.isConnected,
      documents: nodeData.documents || [],
    };
  }

  async getNodeInfo(): Promise<IrohNode> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    const nodeData = await NativeIroh.getNodeInfo();
    return {
      id: nodeData.id,
      isConnected: nodeData.isConnected,
      documents: nodeData.documents || [],
    };
  }

  async shutdown(): Promise<void> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    await NativeIroh.shutdownNode();
    this.progressCallbacks.clear();
  }

  async createDocument(name: string): Promise<IrohDocument> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    const docData = await NativeIroh.createDocument(name);
    return {
      id: docData.id,
      name: docData.name,
      size: docData.size,
      created: new Date(docData.created),
      modified: new Date(docData.modified),
    };
  }

  async getDocument(id: string): Promise<IrohDocument | null> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    const docData = await NativeIroh.getDocument(id);
    if (!docData) {
      return null;
    }

    return {
      id: docData.id,
      name: docData.name,
      size: docData.size,
      created: new Date(docData.created),
      modified: new Date(docData.modified),
    };
  }

  async listDocuments(): Promise<IrohDocument[]> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    const docsData = await NativeIroh.listDocuments();
    return docsData.map((docData: any) => ({
      id: docData.id,
      name: docData.name,
      size: docData.size,
      created: new Date(docData.created),
      modified: new Date(docData.modified),
    }));
  }

  async deleteDocument(id: string): Promise<boolean> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    return await NativeIroh.deleteDocument(id);
  }

  async addBlob(data: Uint8Array): Promise<IrohBlob> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    // Convert Uint8Array to platform-specific format
    const platformData =
      Platform.OS === "ios"
        ? Array.from(data) // iOS expects Array<number>
        : data; // Android can handle Uint8Array directly

    const blobData = await NativeIroh.addBlob(platformData);
    return {
      hash: blobData.hash,
      size: blobData.size,
      format: blobData.format as "raw" | "hash-seq",
    };
  }

  async getBlob(hash: string): Promise<Uint8Array | null> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    const data = await NativeIroh.getBlob(hash);
    if (!data) {
      return null;
    }

    // Convert platform-specific format to Uint8Array
    return new Uint8Array(data);
  }

  async addFile(filePath: string): Promise<IrohBlob> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    const blobData = await NativeIroh.addFile(filePath);
    return {
      hash: blobData.hash,
      size: blobData.size,
      format: blobData.format as "raw" | "hash-seq",
    };
  }

  async exportFile(hash: string, outputPath: string): Promise<boolean> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    return await NativeIroh.exportFile(hash, outputPath);
  }

  async connect(nodeId: string): Promise<boolean> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    return await NativeIroh.connect(nodeId);
  }

  async disconnect(nodeId: string): Promise<boolean> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    return await NativeIroh.disconnect(nodeId);
  }

  async listConnections(): Promise<string[]> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    return await NativeIroh.listConnections();
  }

  async shareDocument(documentId: string): Promise<string> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    return await NativeIroh.shareDocument(documentId);
  }

  async receiveDocument(
    ticket: string,
    onProgress?: (progress: FileTransferProgress) => void,
  ): Promise<IrohDocument> {
    if (!NativeIroh) {
      throw new Error("Iroh native module not available");
    }

    // Register progress callback if provided
    const callbackId = Math.random().toString(36);
    if (onProgress) {
      this.progressCallbacks.set(callbackId, onProgress);
    }

    try {
      const docData = await NativeIroh.receiveDocument(ticket);

      return {
        id: docData.id,
        name: docData.name,
        size: docData.size,
        created: new Date(docData.created),
        modified: new Date(docData.modified),
      };
    } finally {
      // Clean up progress callback
      if (onProgress) {
        this.progressCallbacks.delete(callbackId);
      }
    }
  }
}

/**
 * Check if native Iroh module is available
 */
export function isNativeIrohAvailable(): boolean {
  return !!NativeIroh;
}

/**
 * Get platform-specific module information
 */
export function getNativeModuleInfo(): {
  platform: string;
  available: boolean;
  version?: string;
} {
  return {
    platform: Platform.OS,
    available: isNativeIrohAvailable(),
    version: NativeIroh?.VERSION || undefined,
  };
}

